package main

import (
	"bufio"
	"database/sql"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"regexp"
	"strings"

	"github.com/cheggaaa/pb/v3"
	_ "github.com/denisenkom/go-mssqldb"
	_ "github.com/go-sql-driver/mysql"
)

var (
	C_RE        = regexp.MustCompile(`CREATE TABLE`)
	SQLHOST     string
	SQLPORT     int
	SQLPASSWD   string
	SQLDatabase string
	SQLSchema   string
	SQLUser     string
	SQLFile     string
	GlobalErr   string
)

type SqlFile struct {
	scanner     *bufio.Reader
	InLines     []string
	CreateLines []string
	OtherLines  []string
	Tables      map[string][]string
	END_TAG     string
}

func BatchRun(db *sql.DB, lines, others []string, batchSize int) int64 {
	var affect int64

	// name := strings.Fields(lines[0])[2]
	// pre := fmt.Sprintf("SET IDENTITY_INSERT %s ON\n", name)
	pre := strings.Join(others, "\n")
	oneBatch := pre

	log.Println("<<< Insert :", len(lines))
	defer log.Println(">>>")
	for no, line := range lines {
		if no%batchSize == 0 && no != 0 {
			affect += Exec(db, oneBatch)
			oneBatch = pre
		}
		oneBatch += line + "\n"
	}
	if oneBatch != pre {
		affect += Exec(db, oneBatch)
	}
	return affect

}

func Exec(db *sql.DB, line string) int64 {
	// log.Println(line)
	res, err := db.Exec(line)
	if err != nil && fmt.Sprint(err) != GlobalErr {
		log.Println(err)
		GlobalErr = fmt.Sprint(err)
	}
	return 0
	numAffected, err := res.RowsAffected()
	if err != nil && fmt.Sprint(err) != GlobalErr {
		log.Println(err)
		GlobalErr = fmt.Sprint(err)
	}
	return numAffected
}

func NewSqlReader(f, tp string) (sqlFile *SqlFile, err error) {
	sqlFile = new(SqlFile)
	// fssta, err := os.Stat(f)
	// file, err := os.Open(f)
	// if err != nil {
	// 	return
	// }
	// allSize := fssta.Size()
	// scanner := bufio.NewReader(file)
	// scanner.Split(bufio.ScanLines)
	// sqlFile.scanner = scanner
	// sqlFile.linesToDeal = make(chan string, 1024)

	query := url.Values{}
	query.Add("database", SQLDatabase)
	u := &url.URL{
		Scheme: "sqlserver",
		User:   url.UserPassword(SQLUser, SQLPASSWD),
		Host:   fmt.Sprintf("%s:%d", SQLHOST, SQLPORT),
		// Path:  instance, // if connecting to an instance instead of a port
		RawQuery: query.Encode(),
	}
	// , 1443)
	db, err := sql.Open("sqlserver", u.String())
	defer db.Close()
	if tp == "sqlserver" {
		sqlFile.END_TAG = "GO"
	}
	END_TAG := sqlFile.END_TAG
	bufString := ""
	jumpComment := false
	// var nowSize int64

	// allBuf := make([]byte, allSize+1)
	fmt.Print("reading .....\r")
	allBuf, err := ioutil.ReadFile(f)
	if err != nil {
		return
	}

	lines := strings.Split(string(allBuf), "\n")
	linesNo := len(lines)
	// bar := pb.StartNew(len(lines))
	fmt.Println("reading [ok]")
	for no, line := range lines {
		line := strings.TrimSpace(line)
		// lineBytes, _, lerr := reader.ReadLine()
		// if lerr != nil {
		// 	break
		// }
		// nowSize += int64(len(lineBytes))
		// line := string(lineBytes)
		// bar.Increment()
		if strings.HasPrefix(line, "--") {
			continue
		}
		if jumpComment {
			if strings.Contains(line, "*/") {
				jumpComment = false
			}
			continue
		}
		if strings.HasPrefix(line, "/*") {
			jumpComment = true
			continue
		}
		if no%10000 == 0 {

			fmt.Printf("line : %10d / %10d  \r", no, linesNo)
		}
		if line == END_TAG {
			oneSQLOPER := bufString
			// fmt.Println(bufString)
			if strings.HasPrefix(oneSQLOPER, "CREATE") {
				// sqlFile.CreateLines = append(sqlFile.CreateLines, oneSQLOPER)
				if len(sqlFile.InLines) > 0 {
					BatchRun(db, sqlFile.InLines, sqlFile.OtherLines, 10000)

					sqlFile.InLines = []string{}
					sqlFile.OtherLines = []string{}
				}
				// log.Println(oneSQLOPER)
				Exec(db, oneSQLOPER)
				// fmt.Println("create table: ", oneSQLOPER[13:20])
				// break
			} else {
				if strings.TrimSpace(bufString) != "" {
					if strings.HasPrefix(oneSQLOPER, "INSERT") {
						sqlFile.InLines = append(sqlFile.InLines, oneSQLOPER)
					} else {
						sqlFile.OtherLines = append(sqlFile.OtherLines, oneSQLOPER)
					}

				}

			}
			bufString = ""
		} else {
			bufString += line + "\n"
			// fmt.Println(line)
			// fmt.Println("---")
		}

	}

	return

}

func (sqlFile *SqlFile) ParseTables() {
	bar := pb.StartNew(len(sqlFile.CreateLines))
	for _, line := range sqlFile.CreateLines {
		fmt.Println(line)
		bar.Increment()
	}
}

func main() {
	flag.StringVar(&SQLHOST, "H", "localhost", "sql host")
	flag.StringVar(&SQLUser, "u", "sa", "sql user")
	flag.StringVar(&SQLPASSWD, "P", "yourStrong123Password", "set password")
	flag.StringVar(&SQLDatabase, "D", "", "set database")
	flag.IntVar(&SQLPORT, "p", 1433, "set sql server port")
	flag.StringVar(&SQLSchema, "schema", "sqlserver", "set sql schema")
	flag.StringVar(&SQLFile, "file", "", "set sql file path")
	flag.Parse()
	sql, err := NewSqlReader(SQLFile, SQLSchema)
	if err != nil {
		log.Fatal(err)
	}
	sql.ParseTables()
}
